/*	--- 使用注解装配AOP --- 

上一节我们讲解了【使用AspectJ的注解】，并【配合一个复杂的execution(* xxx.Xyz.*(..))语法】来定义【应该如何装配AOP】。

-----------------------------------------

在【实际项目】中，【这种写法】其实很少使用。

假设你写了一个【SecurityAspect】： */
@Aspect 
@Component 
public class SecurityAspect {

	@Before("execution(public * com.itranswarp.learnjava.service.*.*(..))")
	public void check() {
		if (SecurityContext.getCurrentUser() == null) {
			throw new RuntimeException("check failed");
		}
	}
}

/*
基本能实现【无差别全覆盖】，即【某个包下面的 所有Bean的 所有方法】 都会被【SecurityAspect.check()方法】拦截。

还有的童鞋喜欢用【方法名前缀】进行拦截： */
@Aspect 
@Component
public class SecurityAspect {

	@Around("execution(public * update*(..))")
	public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
		// 对update开头的方法,切换【数据源】
		String old = setCurrentDataSource("master");
		Object relVal = pjp.proceed();
		restoreCurrentDataSource(old);

		return relVal;
	}
}


/*
这种【非精准打击】误伤面更大，因为从【方法前缀】区分【是否是 数据库操作】是【非常不可取的】。

---------------------------------------------


我们在【使用AOP】时，要注意到虽然【Spring容器】可以把【指定的方法】通过【AOP规则】装配到【指定的Bean的指定方法】前后

但是，如果【自动装配】时，因为【不恰当的范围】，容易导致【意想不到的结果】，即很多【不需要AOP代理的Bean】也被【自动代理】了，并且，后续【新增的Bean】，如果不清楚【现有的AOP装配规则】，容易被【强迫装配】。


-------------------------------------

使用AOP时，【被装配的Bean】最好自己能清清楚楚地【知道 自己被安排了】。

例如，【Spring提供的@Transactional】就是一个非常好的例子。如果【我们自己写的Bean】希望【在一个数据库事务中被调用】，就【标注上@Transactional 】： */
@Component 
public class UserService {
	// 有事务
	@Transactional
	public User createUser(String name) {
		//...
	}

	// 无事务
	public boolean isValidName(String name) {
		//...
	}

	// 有事务
	@Transactional 
	public void updateUser(User user) {
		//...
	}
}


/*
或者直接在【class级别】注解，表示“所有public方法都被安排了” ： */
@Component 
@Transactional 
public class UserService {
	//...
}


/*
通过 @Transactional ，【某个方法】是否启用了【事务】就一清二楚了。

因此，【装配AOP】的时候，使用【注解】是【最好的方式】。

我们以【一个实际例子】演示【如何使用注解实现AOP装配】。

为了【监控应用程序的性能】，我们定义【一个性能监控的注解】： */
@Target(METHOD) 
@Retention(RUNTIME)
public @interface MetricTime {
	String value();
}

/*
在【需要被监控的关键方法上】, 标注【该注解】： */
@Component 
public class UserService {
	// 监控register()方法性能
	@MetricTime("register")  // 自动监控 @MetricTime("name")
	public User register(String email, String password, String name) {
		//...
	}
	//...
}


/*
然后，我们定义【MetricAspect】： */
@Aspect 
@Component
public class MetricAspect {

	@Around("@annotation(metricTime)")
	public Object metric(ProceedingJoinPoint joinPoint, MetricTime metricTime) throws Throwable {

		String name = metricTime.value();
		long start = System.currentTimeMillis();

		try {
			return joinPoint.proceed();
		} finally {
			long t = System.currentTimeMillis() - start;

			// 写入日志/发送至JDK
			System.err.println("[Metrics]" + name + ": " + t + "ms");
		}
	}
}

/*
注意【metric()方法】标注了 @Around("@annotation(metricTime)")，

@Around("@annotation(metricTime)" 
	的意思是，【符合条件的目标方法】是【带有@MetricTime注解的方法】

因为【metric()方法】参数类型是【MetricTime】（注意【参数名是metricTime】不是MetricTime），我们通过【它】获取【性能监控的名称】。



有了【@MetricTime注解】，再配合【MetricAspect】，【任何Bean】，只要【方法】标注了【@MetricTime注解】，就可以【自动实现性能监控】。


运行代码，【输出结果】如下：
----------------------------------------------
Welcome, Bob!
[Metrics] register: 16ms

-------------------------------------------------


==================================================================

#	练习

使用注解+AOP实现性能监控 */


/*==============================================================

#	----- 使用注解装配AOP の 小结 ----- 


1. 使用【注解】实现【AOP】需要【先定义注解】，然后使用 
	@Around("@annotation(name)")
实现【装配】；

2. 使用【注解】既简单，又能明确标识【AOP装配】，是【使用AOP】推荐的方式。


*/



























